package cn.lg.soar.system.general.service.impl;

import cn.lg.soar.common.util.ParameterUtil;
import cn.lg.soar.core.util.SoarSubscriber;
import cn.lg.soar.database.base.service.impl.BaseServiceImpl;
import cn.lg.soar.database.util.DataSourceUtils;
import cn.lg.soar.system.general.entity.DataSource;
import cn.lg.soar.system.general.mapper.DataSourceMapper;
import cn.lg.soar.system.general.service.IDataSourceService;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Lazy;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

/**
 * DataSource服务层实现
 * 数据源
 * @author luguoxiang
 * @since 2022-04-30
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@Lazy(false)
public class DataSourceServiceImpl extends BaseServiceImpl<DataSourceMapper, DataSource> implements IDataSourceService {

    protected static final Logger LOGGER = LoggerFactory.getLogger(DataSourceServiceImpl.class);

    @Autowired
    private DynamicRoutingDataSource dynamicRoutingDataSource;
    @Autowired
    private DynamicDataSourceProperties dynamicDataSourceProperties;

    public DataSourceServiceImpl() {
        // 启动时初始化所有数据源
        SoarSubscriber.subscribeRunner(applicationContext -> {
            CompletableFuture.runAsync(() -> {
                try {
                    refresh();
                } catch (Exception e) {
                    log.error("刷新数据源失败", e);
                }
            });
        }, -1);
    }

    /**
     * 刷新数据源
     * @param dataSource
     */
    @Override
    public void refresh(DataSource ...dataSources) {
        if (dataSources.length == 0) {
            List<DataSource> list = list(
                    Wrappers.<DataSource>lambdaQuery().eq(DataSource::getDisabled, false)
            );
            for (DataSource dataSource : list) {
                refresh(dataSource);
            }
        } else {
            for (DataSource dataSource : dataSources) {
                refresh(dataSource);
            }
        }
    }

    @Override
    public boolean removeById(Serializable id) {
        ParameterUtil.notNull(id, "id不能为空");
        if (!id.equals(dynamicDataSourceProperties.getPrimary())) {
            dynamicRoutingDataSource.removeDataSource(id.toString());
        }
        return super.removeById(id);
    }

    @Override
    public boolean removeBatchByIds(Collection<?> list, int batchSize) {
        for (Object id : list) {
            ParameterUtil.notNull(id, "id不能为空");
            if (!id.equals(dynamicDataSourceProperties.getPrimary())) {
                dynamicRoutingDataSource.removeDataSource(id.toString());
            }
        }
        return super.removeBatchByIds(list, batchSize);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean updateById(DataSource entity) {
        ParameterUtil.notNull(entity.getId(), "id不能为空");
        boolean present = Optional.ofNullable(entity.getUsername())
                .filter(u -> u.length() == 6 && u.indexOf("****") == 1)
                .isPresent();
        if (present) {
            entity.setUsername(null);
        }
        if ("******".equals(entity.getPassword())) {
            entity.setPassword(null);
        }
        if (super.updateById(entity)) {
            refresh(getById(entity.getId()));
            return true;
        }
        return false;
    }

    @Override
    public boolean save(DataSource entity) {
        String id = entity.getId();
        ParameterUtil.notNull(id, "id不能为空");
        ParameterUtil.isFalse(
                id.equals(dynamicDataSourceProperties.getPrimary()),
                "不能使用主数据源id：{0}",
                id
        );
        if (super.save(entity)) {
            refresh(entity);
            return true;
        }
        return false;
    }

    private void refresh(DataSource dataSource) {
        String id = dataSource.getId();
        ParameterUtil.notNull(id, "id不能为空");
        ParameterUtil.isFalse(
                id.equals(dynamicDataSourceProperties.getPrimary()),
                "不能使用主数据源id：{0}",
                id
        );
        // 移除关闭数据源
        try {
            dynamicRoutingDataSource.removeDataSource(dataSource.getId());
        } catch (RuntimeException ignored) {

        }
        if (Boolean.TRUE.equals(dataSource.getDisabled())) {
            return;
        }
        // 生成数据源配置
        DataSourceProperties dataSourceProperties = new DataSourceProperties();
        BeanUtils.copyProperties(dataSource, dataSourceProperties, "type");
        try {
            dataSourceProperties.setType((Class<? extends javax.sql.DataSource>) Class.forName(dataSource.getType()));
        } catch (ClassNotFoundException | RuntimeException e) {
            LOGGER.warn("未配置数据源类型:{}", dataSource.getId(), e);
        }
        // 添加数据源
        dynamicRoutingDataSource.addDataSource(
                dataSource.getId(),
                DataSourceUtils.getDataSource(dataSourceProperties)
        );
    }
}
